package com.jhh.pay.center.search;

import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.IndicesAdminClient;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.rest.RestStatus;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.util.Map;

import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;

/**
 * 搜索抽象类
 *
 * @param <T>
 * @author tangxd
 */
@Slf4j
public abstract class AbstractElasticSearcher<T extends AbstractMapping> implements Searcher<T> {

    @Autowired
    protected IndicesAdminClient adminClient;

    @Autowired
    protected Client client;

    protected abstract IndexRouter getRouter();

    protected abstract String getIdxName();

    protected abstract String getIdxAlias();

    protected abstract String getIdxTypeName();

    protected abstract DocTransformer<T, String> mappingTransformer();

    protected abstract DocTransformer<String, T> sourceTransformer();

    public abstract Map<String, Object> initSetting();

    @Override
    public boolean existIndex() {
        IndicesExistsRequest ir = new IndicesExistsRequest(getIdxName());
        IndicesExistsResponse resp = adminClient.exists(ir).actionGet();
        return resp.isExists();
    }

    @Override
    public boolean init() {
        if (existIndex()) {
            return false;
        }
        boolean ok = initIndex();
        if (!ok) {
            return false;
        }
        putMapping();
        return true;
    }

    private boolean initIndex() {
        CreateIndexRequest req = new CreateIndexRequest(this.getIdxName());
        req.alias(new Alias(getIdxAlias()));
        req.settings(initSetting());
        CreateIndexResponse resp = adminClient.create(req).actionGet();
        return resp.isAcknowledged();
    }

    @Override
    public boolean deleteIndex() {
        DeleteIndexRequest req = new DeleteIndexRequest(this.getIdxAlias());
        DeleteIndexResponse resp = adminClient.delete(req).actionGet();
        return resp.isAcknowledged();
    }

    @Override
    @SuppressWarnings("unchecked")
    public boolean index(T obj, boolean refresh) {
        IndexRequest req = new IndexRequest(this.getIdxAlias(), getIdxTypeName());
        String source = mappingTransformer().transform(obj);
        req.source(source, XContentType.JSON);
        req.id(obj.getId().toString());
        if (getRouter() != null) {
            req.routing(getRouter().routing(obj.getId()));
        }
        if (refresh) {
            req.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
        }
        IndexResponse resp;
        resp = client.index(req).actionGet();
        if (resp.status() == RestStatus.OK || resp.status() == RestStatus.CREATED) {
            obj.setVersion(resp.getVersion());
            return true;
        }
        return false;
    }

    @Override
    @SuppressWarnings("unchecked")
    public T get(String id) {
        GetRequest req = new GetRequest(this.getIdxAlias(), getIdxTypeName(), id);
        if (getRouter() != null) {
            req.routing(getRouter().routing(id));
        }
        GetResponse resp = client.get(req).actionGet();

        if (resp.isExists()) {
            String source = resp.getSourceAsString();
            T m = sourceTransformer().transform(source);
            if (null != m) {
                m.setVersion(resp.getVersion());
                return m;
            }
        }
        return null;
    }

    @Override
    @SuppressWarnings("unchecked")
    public boolean delete(String id) {
        DeleteRequest req = new DeleteRequest(this.getIdxAlias(), getIdxTypeName(), id);
        if (getRouter() != null) {
            req.routing(getRouter().routing(id));
        }
        DeleteResponse resp = client.delete(req).actionGet();
        return resp.status() == RestStatus.OK;

    }

    @Override
    @SuppressWarnings("unchecked")
    public boolean update(String id, Map<String, Object> updateProperties) {
        if (updateProperties.size() <= 0) {
            return true;
        }
        UpdateRequest updateRequest = new UpdateRequest(this.getIdxAlias(), getIdxTypeName(), id);
        if (getRouter() != null) {
            updateRequest.routing(getRouter().routing(id));
        }
        try {
            XContentBuilder builder = jsonBuilder().startObject();
            for (Map.Entry<String, Object> entry : updateProperties.entrySet()) {
                builder = builder.field(entry.getKey(), entry.getValue());
            }
            builder = builder.endObject();
            updateRequest.doc(builder);
            UpdateResponse resp = client.update(updateRequest).actionGet();
            return resp.status() == RestStatus.OK;
        } catch (IOException e) {
            e.printStackTrace();
            log.error(e.getMessage(), e);
        }
        return false;
    }
}
